package de.jmda.mview.typeshape.relation;

import java.awt.Point;
import java.awt.geom.Line2D;
import java.util.LinkedList;
import java.util.List;

import de.jmda.mview.typeshape.RectangleSide;

/**
 * <b>R</b>elation <b>E</b>ndpoint <b>C</b>onnection (REC) connecting source
 * with target connector point in <b>O</b>rthogonal routing style. Both relation
 * endpoints initially have <b>V</b>ertical direction (V2V) and are initially
 * facing each other.
 *
 * @author roger.jmda@gmail.com
 */
public class RECOV2VFacing
    extends RECOV2V
{
	private List<Line2D> lines;

	/**
	 * @param relationEndpointHelperSource
	 * @param relationEndpointHelperTarget
	 */
	public RECOV2VFacing(
			RelationEndpointHelper relationEndpointHelperSource,
	    RelationEndpointHelper relationEndpointHelperTarget)
	    throws IllegalArgumentException
	{
		super(relationEndpointHelperSource, relationEndpointHelperTarget);
	}
	
	@Override
	protected void checkStateInvalidated() throws StateInvalidatedException
	{
		super.checkStateInvalidated();

	  // check if relation endpoints are facing each other
		if (false ==
		    relationEndpointHelperSource.connectorPointsFacing(
		    		relationEndpointHelperTarget))
		{
			throw new StateInvalidatedException(
					"relation endpoints have to be facing each other");
		}
	}

	@Override
	protected List<Line2D> getLines()
	{
		if (lines == null)
		{
			lines = new LinkedList<Line2D>();
		}
		return lines;
	}

	/**
	 * The relation endpoint connections are updated to have
	 * <ul>
	 *   <li>no additional connector line if both relation endpoints have the same
	 *       x coordinate and
	 *   <li>one additional connector line otherwise. The lenght of both relation
	 *       endpoints is adjusted accordingly if necessary.
	 * </ul>
	 *
	 * @see de.jmda.mview.typeshape.relation.RelationEndpointConnection#update()
	 */
	@Override
  protected void update() throws StateInvalidatedException
  {
		boolean recoveryAttemptSuccessful = false;

		try
		{
			super.update();
		}
		catch (StateInvalidatedException e)
		{
			recoveryAttemptSuccessful = recoveryAttemptSuccessful();

			if (recoveryAttemptSuccessful == false)
			{
				throw e;
			}
		}

		if (recoveryAttemptSuccessful == false)
		{
			// this means recovery was not attempted (otherwise exception would have
			// been thrown)
			updateRelationEndpointLength();
		}

		// just shortcuts
		Point sourceConnPoint = relationEndpointHelperSource.getConnectorPoint();
		Point targetConnPoint = relationEndpointHelperTarget.getConnectorPoint();

		List<Line2D> lines = getLines();

		lines.clear();
		
		if (sourceConnPoint.x != targetConnPoint.x)
		{
			// add connector line
			lines.add(
					new Line2D.Double(
							sourceConnPoint.x, sourceConnPoint.y,
							targetConnPoint.x, targetConnPoint.y));
		}
  }

	/**
	 * This method tries to adjust the length of both relation endpoints so that
	 * they will be facing each other when this method returns.
	 * <p>
	 * <b>Important</b>
	 * <p>
	 * Calls this method only after super.{@link #update()} threw {@link
	 * StateInvalidatedException}!
	 * 
	 * @return <code>true</code> if adjustment was successful, <code>false</code>
	 *         otherwise
	 */
	private boolean recoveryAttemptSuccessful()
	{
		// just shortcuts
		Point sourceAnchorPoint = relationEndpointHelperSource.getAnchorPoint();
		Point targetAnchorPoint = relationEndpointHelperTarget.getAnchorPoint();
		
		RectangleSide sourceAnchorSide = relationEndpointHelperSource.getAnchorSide();
		RectangleSide targetAnchorSide = relationEndpointHelperTarget.getAnchorSide();
		
		if (((sourceAnchorSide == RectangleSide.BOTTOM) &&
				 (targetAnchorSide == RectangleSide.TOP   )) ||
				((sourceAnchorSide == RectangleSide.TOP   ) &&
				 (targetAnchorSide == RectangleSide.BOTTOM)))
		{
			if ((sourceAnchorSide == RectangleSide.BOTTOM) &&
					(sourceAnchorPoint.y >= targetAnchorPoint.y))
			{
				//      |
				//     ---
				// ---
				//  |
				return false;
			}
			else if ((sourceAnchorSide == RectangleSide.TOP) &&
					     (sourceAnchorPoint.y <= targetAnchorPoint.y))
			{
				//  | 
				// ---
				//     ---
				//      |
				return false;
			}
			else
			{
				updateRelationEndpointLength();
				
				return true;
			}
		}

		return false;
	}

	/**
	 * calculate new relation endpoint length based on anchor points
	 */
	private void updateRelationEndpointLength()
	{
		// just shortcuts
		Point sourceAnchorPoint = relationEndpointHelperSource.getAnchorPoint();
		Point targetAnchorPoint = relationEndpointHelperTarget.getAnchorPoint();

		int yDistanceAnchorPoints;
	
		if (sourceAnchorPoint.y <= targetAnchorPoint.y)
		{
			yDistanceAnchorPoints = targetAnchorPoint.y - sourceAnchorPoint.y;
		}
		else
		{
			// sourceAnchorPoint.y > targetAnchorPoint.y
			yDistanceAnchorPoints = sourceAnchorPoint.y - targetAnchorPoint.y;
		}
		
		int newLength;
		
		if (yDistanceAnchorPoints == 0)
		{
			newLength = 0;
		}
		else
		{
			newLength = yDistanceAnchorPoints / 2;
		}

		// implicit synchronisation
		relationEndpointHelperSource.setLength(newLength);
		relationEndpointHelperTarget.setLength(newLength);
	}
}